package xyz.gzdxc.common.interceptor;


import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import xyz.gzdxc.common.annotation.OperPriv;
import xyz.gzdxc.common.constant.AuthConstant;
import xyz.gzdxc.common.constant.ResultCode;
import xyz.gzdxc.common.mysql.entity.UserTokenEntity;
import xyz.gzdxc.common.service.SessionService;
import xyz.gzdxc.common.service.UserPrivService;
import xyz.gzdxc.common.service.UserService;
import xyz.gzdxc.common.util.CookieUtil;
import xyz.gzdxc.common.util.ResultBuilder;
import xyz.gzdxc.common.vo.response.CheckSessionResp;
import xyz.gzdxc.common.vo.response.ResultVo;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Component
public class AuthHandlerInterceptor implements HandlerInterceptor {
    private static final Logger LOG = LoggerFactory.getLogger(AuthHandlerInterceptor.class);

    @Autowired
    SessionService sessionService;

    @Autowired
    UserService userService;

    @Autowired
    UserPrivService userPrivService;

    private final ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler){
        if(handler instanceof HandlerMethod){
            HandlerMethod method = (HandlerMethod)handler;
            String session = null;
            Cookie cookie = CookieUtil.getCookie(request, AuthConstant.SESSION_KEY);
            if(cookie == null){
                LOG.info("the cookie is null");
                returnResultVO(response, ResultBuilder.build(ResultCode.UNAUTHORIZED));
                return false;
            }
            session = cookie.getValue();
            LOG.info("the session => {}", session);
            //judge the session whether valid or not
            CheckSessionResp checkSessionResp;
            checkSessionResp = sessionService.checkSession(session);
            LOG.info("checkSessionResp => {}", checkSessionResp);
            if(checkSessionResp.isTimeOutFlag()){
                LOG.info("login timeout");
                returnResultVO(response, ResultBuilder.build(ResultCode.OUT_TIME));
                return false;
            }
            if(checkSessionResp.isSessionTimeOutFlag()){
                LOG.info("session timeout");
                returnResultVO(response, ResultBuilder.build(ResultCode.INVALID_SESSION));
                return false;
            }
            //get the user data to the request
            UserTokenEntity userToken = userService.getUserToken(session);
            if(userToken == null){
                LOG.info("the userToken is null");
                returnResultVO(response, ResultBuilder.build(ResultCode.ACCOUNT_NOT_EXISTENT));
                return false;
            }
            LOG.info("userToken => {}", userToken);
            request.setAttribute(AuthConstant.USER_TOKEN_KEY, userToken);
            //according to the annotation configuration, check the user whether has the priv
            OperPriv operPriv = method.getMethodAnnotation(OperPriv.class);
            if(operPriv != null){
                String oper = operPriv.oper();
                LOG.info("oper => {}", oper);
                boolean hasPriv = true;
                //judge the priv
                hasPriv = userPrivService.hasPriv(userToken.getId(), oper);
                LOG.info("hasPriv => {}", hasPriv);
                if(!hasPriv){
                    LOG.info("not has priv");
                    returnResultVO(response, ResultBuilder.build(ResultCode.FORBIDDEN));
                    return false;
                }
            }
            return true;
        }
        LOG.info("the handler is not a method in back end");
        return false;
    }

    private void returnResultVO(HttpServletResponse response, ResultVo resultVo){
        response.setContentType("application/json");
        response.setCharacterEncoding("UTF-8");
        try {
            response.getWriter().write(objectMapper.writeValueAsString(resultVo));
        } catch (IOException e) {
            LOG.info("Exception => {}", e.toString());
            e.printStackTrace();
        }
    }
}
